#pragma once
#include <iostream>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>

#include "InetAddr.hpp"
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "ThreadPool.hpp"

#define BACKLOG 8

using namespace LogModule;
using namespace LockModule;
using namespace CondModule;
using namespace ThreadPoolModule;

const static uint16_t gport = 8080;

class TcpServer
{
    using task_t = std::function<void()>;

    struct ThreadData
    {
        int sockfd;
        TcpServer* self;
    };
public:
    TcpServer(int port = gport)
        : _port(port), _isrunning(false)
    {
    }
    void InitServer()
    {
        // 1.创建 tcp socket
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error " << strerror(errno);
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket create success , listensockfd is :" << _listensockfd;

        // 2.bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // 不能对云服务器的公网ip做bind，也不建议服务器bind任意一个固定的Ip,0为任意地址bindM

        int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error " << strerror(errno);
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success ";

        // 3.tcp是面向连接的，所以tcp就要求随时待命连接
        // tcp需要将socket设置成为监听状态
        n = listen(_listensockfd, BACKLOG);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error " << strerror(errno);
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success ";

        // 4.设置非阻塞
        // ::signal(SIGCHLD,SIG_IGN); // 子进程退出时，os会自动回收资源
    }

    void HandlerRequest(int sockfd) // TCP也是全双工通信的
    {
        char inbuffer[4096];
        //长任务
        while (true)
        {
            //面向字节流的，所以可以用read,write，udp是面向数据报的，所以不可以这样
            // ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);  //面向字节流的read读取是不完全的，十个字节可能只收到9个
            ssize_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);  
            if (n > 0)
            {
                LOG(LogLevel::INFO) << inbuffer;

                inbuffer[n] = 0;
                std::string echo_str("server echo# ");
                echo_str += inbuffer;

                // ::write(sockfd, echo_str.c_str(), echo_str.size()); //面向字节流的write写入是不完全的，十个字节可能只写入9个
                ::send(sockfd, echo_str.c_str(), echo_str.size(), 0); 
            }
            else if(n == 0)
            {
                // 客户端关闭连接
                LOG(LogLevel::INFO) << "client close the connection, sockfd is " << sockfd;
                break;
            }
            
            else    
            {
                // 读错误
                LOG(LogLevel::WARNING) << "read error " << strerror(errno);
                break;
            }
        }
        ::close(sockfd); //不关闭会出现 文件描述符泄露问题！
    }
    static void *ThreadEntry(void *args) //类内带this指针
    {
        pthread_detach(pthread_self());
        ThreadData* data = (ThreadData*)args;
        data->self->HandlerRequest(data->sockfd);
        return nullptr;
    }
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 不能直接读取数据
            // 1.获取新连接
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            LOG(LogLevel::DEBUG) << "accept ing...";
            //我们要获取client的信息: 数据（sockfd）+client的socket信息(accept || recvfrom)
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept warning " << strerror(errno);
                continue;
            }
            // 获取连接成功
            LOG(LogLevel::INFO) << "accept success , sockfd is :" << sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "client info: " << addr.Addr();

            // // version --0 debug
            // HandlerRequest(sockfd);

            // // version --1 多进程版本
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //child
            //     //子进程继承父进程的文件描述符表，两张，父子各一张
            //     // 1.关闭监听套接字(关闭子进程不需要的fd)
            //     ::close(_listensockfd);
            //     if(fork() > 0)
            //         exit(0); //子进程退出了
            //     //孙子进程继续执行-》成了孤儿进程—》会被系统pid = 1领养
            //     HandlerRequest(sockfd);
            //     exit(0);
            // }
            // //parent
            // // 也要关闭父进程不需要的fd
            // ::close(sockfd);
            // int rid = waitpid(id,nullptr,0);
            // if(rid < 0)
            // {
            //     LOG(LogLevel::WARNING) << "waitpid error " << strerror(errno);
            // }

            // // version --2 多线程版本
            //主线程与新线程共享同一张文件描述符表
            // pthread_t tid;
            // ThreadData * data = new ThreadData;
            // data->sockfd = sockfd;
            // data->self = this;
            // pthread_create(&tid,nullptr,ThreadEntry,data);

            // // version --3 线程池版本 适合处理短任务，或者用户量较少
            task_t f = std::bind(&TcpServer::HandlerRequest, this, sockfd); //构建任务
            ThreadPool<task_t>::GetInstance()->Enqueue(f);
        }
        
    }
    void Stop()
    {
        _isrunning = false;
    }
    ~TcpServer() {}

private:
    int _listensockfd; // 监听套接字
    uint16_t _port;
    bool _isrunning;
};